package cn.melonlib.cores.graphql.config.bean;

import cn.melonlib.cores.commons.comp.service.impl.WorkerBasicService;
import cn.melonlib.cores.commons.comp.utils.EntityWrapperContainer;
import cn.melonlib.cores.commons.model.entity.IEntity;
import cn.melonlib.cores.graphql.comp.config.WorkerParametersReader;
import cn.melonlib.cores.graphql.comp.service.GraphQLService;
import cn.melonlib.cores.graphql.config.bean.impl.*;
import cn.melonlib.cores.graphql.model.input.GraphQLOrderBy;
import cn.melonlib.cores.jpa.model.bean.collections.JsonMap;
import cn.melonlib.cores.model.anno.WorkerService;
import cn.melonlib.cores.model.input.OrderByInput;
import graphql.schema.DataFetchingEnvironment;
import org.apache.commons.lang3.ObjectUtils;
import org.hibernate.service.spi.ServiceException;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public interface ParameterReader<R> {
    R read(String fieldName, DataFetchingEnvironment env, Class<R> cls) throws Exception;

    public default List<Object> readParams(List<String> paramNames, Method method, DataFetchingEnvironment env, GraphQLService graphQLService,Object bean) throws Exception {
        List<String> names = paramNames.stream().collect(Collectors.toList());
        List<Parameter> parameters = Arrays.stream(method.getParameters()).collect(Collectors.toList());
        List<Object> params = new ArrayList<>();
        WorkerParametersReader workerParametersReader=graphQLService.getWorkerParametersReader();
        WorkerService service = AnnotationUtils.findAnnotation(AopUtils.getTargetClass(bean), WorkerService.class);
        for (int i = 0; i < parameters.size(); i++) {
            if (Map.class.isAssignableFrom(parameters.get(i).getType())) {
                JsonMap<Object> param = workerParametersReader.<MapReader>getReader(Map.class).read(names.get(i), env, Map.class);
                params.add(param);
            }
            if (Integer.class.isAssignableFrom(parameters.get(i).getType())) {
                Integer param = workerParametersReader.<IntegerReader>getReader(Integer.class).read(names.get(i), env, Integer.class);
                params.add(param);
            }
            if (Long.class.isAssignableFrom(parameters.get(i).getType())) {
                Number param = ObjectUtils.<Number>defaultIfNull(workerParametersReader.<FloatReader>getReader(Long.class).read(names.get(i), env, Number.class), null);
                if (ObjectUtils.isNotEmpty(param)) {
                    params.add(param.longValue());
                }
            }
            if (Float.class.isAssignableFrom(parameters.get(i).getType())) {
                Number param = ObjectUtils.defaultIfNull(workerParametersReader.<FloatReader>getReader(Float.class).read(names.get(i), env, Number.class), null);
                if (ObjectUtils.isNotEmpty(param)) {
                    params.add(param.floatValue());
                }
            }
            if (Double.class.isAssignableFrom(parameters.get(i).getType())) {
                Number param = ObjectUtils.defaultIfNull(workerParametersReader.<FloatReader>getReader(Double.class).read(names.get(i), env, Number.class), null);
                if (ObjectUtils.isNotEmpty(param)) {
                    params.add(param);
                }
            }
            if (Boolean.class.isAssignableFrom(parameters.get(i).getType())) {
                Boolean param = ObjectUtils.defaultIfNull(workerParametersReader.<BooleanReader>getReader(Boolean.class).read(names.get(i), env, Boolean.class), null);
                if (ObjectUtils.isNotEmpty(param)) {
                    params.add(param);
                }
            }
            if (String.class.isAssignableFrom(parameters.get(i).getType())) {
                String param = workerParametersReader.<StringReader>getReader(String.class).read(names.get(i), env, String.class);
                params.add(param);
            }

            if(GraphQLOrderBy.class.isAssignableFrom(parameters.get(i).getType())){
                GraphQLOrderBy param = workerParametersReader.<OrderByReader>getReader(GraphQLOrderBy.class).read(names.get(i), env, GraphQLOrderBy.class);
                params.add(param);
            }
            if(OrderByInput.class.isAssignableFrom(parameters.get(i).getType())){
                OrderByInput param = workerParametersReader.<OrderByInputReader>getReader(OrderByInput.class).read(names.get(i), env, OrderByInput.class);
                params.add(param);
            }

            if (List.class.isAssignableFrom(parameters.get(i).getType())) {
                List<?> param = workerParametersReader.<ListReader>getReader(List.class).read(names.get(i), env, List.class);
                if (param.size() == 0) {
                    params.add(param);
                } else if (param.get(0) instanceof Map) {
                    Class<? extends IEntity> type = service.model().inputType() == IEntity.class ? service.model().modelType() : service.model().inputType();
                    List<? extends IEntity> list=param.stream().map(map -> {
                        try {
                            return graphQLService.getEntityWrapperContainer().wrap(EntityWrapperContainer.JSON, graphQLService.getJsonMapper().writeValueAsString(map), type);
                        } catch (Exception e) {
                            throw new ServiceException("数据转换错误");
                        }
                    }).collect(Collectors.toList());
                    params.add(list);
                }else{
                    params.add(param);
                }

            }

            if (IEntity.class.isAssignableFrom(parameters.get(i).getType())) {
                Class entityType = parameters.get(i).getType();
                Class gqlType = null;
                if (IEntity.class.equals(entityType)) {
                    gqlType = AnnotationUtils.findAnnotation(AopUtils.getTargetClass(bean), WorkerService.class).model().modelType();
                    ResolvableType gqlServiceType = ResolvableType.forClass(AopUtils.getTargetClass(bean)).getSuperType();
                    while (!gqlServiceType.resolve().equals(WorkerBasicService.class)) {
                        gqlServiceType = gqlServiceType.getSuperType();
                    }
                    entityType = (Class) gqlServiceType.getGeneric(0).resolve();
                }
                IEntity entity = workerParametersReader.<EntityReader>getReader(IEntity.class).read(names.get(i), env, entityType);
                params.add(entity);
            }
        }
        return params;
    }
}
